﻿Imports System.Data.SqlClient
Imports System.Web.Configuration

Namespace Account
    Public Class Management
        ' Everything related to account management from logging in to logging out should comes from this class.

        ' To check if a user is logged in use the following:
        ' GuildCMS.Account.Management.CheckLogin(loginRequired)
        ' loginRequired = True or False if user must be logged in to view or utilize a function within a page.

        ' Once tested role checking will be added to this class as well...
        ' Add user information retrieval functions main character name to display and so on...

#Region " User login and account status checking "

        ''' <summary>
        ''' Set the database connection variables used throughout this class.
        ''' </summary>
        ''' <remarks>Placed here so they will not need to be typed out each time they are needed.</remarks>
        Private Shared connectionString As String = WebConfigurationManager.ConnectionStrings("SqlConnectionString").ConnectionString()
        Private Shared connection As SqlConnection = New SqlConnection(connectionString)
        Private Shared command As New SqlCommand

        ''' <summary>
        ''' Checks the login status and role of the visitor to the site.
        ''' </summary>
        ''' <param name="loginRequired">if set to <c>true</c> then a user is required to be logged in to view this page.</param>
        ''' <returns>Returns true or false if a visitor is logged in or not.</returns>
        Public Shared Function CheckLogin(ByVal loginRequired As Boolean) As Boolean
            ' This class checks that a user is logged in and if so updates their status within the database for member tracking.
            ' This class will also check the role of a user allowing or disallowing access to certain functions available only to certain roles.
            ' This function should be called on all pages contained within the project.

            Dim memberId As Integer = Nothing

            ' If the member is logged in via a cookie and not session set their session to show they are logged in.
            Dim cookie As HttpCookie = HttpContext.Current.Request.Cookies("Account")
            ' Check if the cookie exists.
            If Not cookie Is Nothing Then
                If Not HttpContext.Current.Session("LoggedIn") = True And cookie.Values("LoggedIn") = True Then
                    If LoginViaCookie(cookie.Values("MemberId"), cookie.Values("CookieCode")) = True Then
                        ' Member was logged in via cookie and is now marked logged into their session as well so update the members status.
                        UpdateStatus(HttpContext.Current.Session("MemberId"))
                        Return True
                        Exit Function
                    Else
                        ' If the cookie code did not match the one from the database destroy the cookie.
                        cookie.Expires = DateTime.Now.AddDays(-1)

                        ' Redirect the user to the login page if login to access this page is required if not just mark as logged in equals false.
                        If loginRequired = True Then
                            HttpContext.Current.ApplicationInstance.Response.Redirect("~/Account/Login.aspx?returnpage=" & HttpContext.Current.Request.Path)
                            Return False
                            Exit Function
                        End If
                        Return False
                    End If
                End If
            End If

            ' Check session values to see if member is logged in.
            If Not HttpContext.Current.Session("LoggedIn") = True Then
                If loginRequired = True Then
                    HttpContext.Current.ApplicationInstance.Response.Redirect("~/Account/Login.aspx?returnpage=" & HttpUtility.UrlEncode(HttpContext.Current.Request.Path))
                    Return False
                    Exit Function
                End If
                Return False
                Exit Function
            Else
                ' Set a variable containing the member's id since we know they are logged in.
                memberId = HttpContext.Current.Session("MemberId")
                ' Since the user is logged in update their status in the database.
                UpdateStatus(memberId)
            End If

            ' Now that we know the member is logged in let's make sure their account is fully functional.

            ' Check to make sure the member's account has not been locked by an administrator.
            If CheckLock(memberId) = True Then
                HttpContext.Current.ApplicationInstance.Response.Redirect("~/Account/Locked.aspx")
                ' Log out the locked member.
                LogoutMember()
                Return False
                Exit Function
            End If

            ' Check if the member's account has been activated yet if not redirect the member to the activation page.
            If Not HttpContext.Current.Request.Path = "/Account/Activate.aspx" And CheckActive(memberId) = False Then
                HttpContext.Current.ApplicationInstance.Response.Redirect("~/Account/Activate.aspx")
                Return False
                Exit Function
            End If

            ' Check that member has a main character associated with their account and if not rediect them to their profile page.
            If Not HttpContext.Current.Request.Path = "/Account/Default.aspx" And GetMain(memberId) = 0 Then
                HttpContext.Current.ApplicationInstance.Response.Redirect("~/Account/Default.aspx")
                Return False
                Exit Function
            End If

            ' Redirect the successfully logged in user to the page they came from initially if they did not access the login page directly.
            If Not HttpContext.Current.Request.QueryString("returnpage") Is Nothing Then
                HttpContext.Current.ApplicationInstance.Response.Redirect(HttpUtility.UrlDecode(HttpContext.Current.Request.QueryString("returnpage")))
            End If
            Return True
        End Function

        ''' <summary>
        ''' Sets session variables to show user as logged in if they are logging in via a cookie.
        ''' </summary>
        ''' <param name="memberId">The member's id number.</param>
        ''' <param name="cookieCode">The cookie code supplied from the cookie.</param>
        Private Shared Function LoginViaCookie(ByVal memberId As Integer, ByVal cookieCode As String)
            ' Check that cookie contains the proper information.
            Try
                ' Get the stored cookie code from the database.
                command = New SqlCommand("sp_public_getCookieCode", connection)
                command.CommandType = CommandType.StoredProcedure
                command.Parameters.Add("@Member_Id", SqlDbType.Bit)
                command.Parameters("@Member_Id").Value = memberId
                command.Parameters.Add("@CookieCode", SqlDbType.NChar, 25)
                command.Parameters("@CookieCode").Direction = ParameterDirection.Output
                connection.Open()
                command.ExecuteNonQuery()
                ' If the member's account is not activated return false.
                If CStr(command.Parameters("@CookieCode").Value) = cookieCode Then
                    ' Set the member's id and logged in as true in their session if checks passed.
                    HttpContext.Current.Session("MemberId") = memberId
                    HttpContext.Current.Session("LoggedIn") = True
                    HttpContext.Current.Session("MainCharater") = GetMain(memberId)
                    Return True
                End If
            Catch ex As Exception

            Finally
                connection.Close()
            End Try
            ' If we got this far the user's cookie appears invalid.
            Return False
        End Function

        ''' <summary>
        ''' Checks if a member's account is marked as activated or not activated.
        ''' </summary>
        ''' <param name="memberId">The member's id number.</param>
        ''' <returns>True or false if the account is activated or not.</returns>
        Private Shared Function CheckActive(ByVal memberId As Integer) As Boolean
            Try
                command = New SqlCommand("sp_public_checkActiveAccount", connection)
                command.CommandType = CommandType.StoredProcedure
                command.Parameters.Add("@Member_Id", SqlDbType.Int)
                command.Parameters("@Member_Id").Value = memberId
                command.Parameters.Add("@Active", SqlDbType.Bit)
                command.Parameters("@Active").Direction = ParameterDirection.Output
                connection.Open()
                command.ExecuteNonQuery()
                ' If the member's account is not activated return false.
                If CBool(command.Parameters("@Active").Value) = False Then
                    Return False
                End If
            Catch ex As Exception
                ' If an exception occurs return false.
                Return False
            Finally
                connection.Close()
            End Try
            ' If the member's account is marked active return true.
            Return True
        End Function

        ''' <summary>
        ''' Checks if a member's account has been locked administrativly.
        ''' </summary>
        ''' <param name="memberId">The member's id number.</param>
        ''' <returns>True or false if the members account has been locked administrativly.</returns>
        Private Shared Function CheckLock(ByVal memberId As Integer) As Boolean
            Try
                command = New SqlCommand("sp_public_checkLockedAccount", connection)
                command.CommandType = CommandType.StoredProcedure
                command.Parameters.Add("@Member_Id", SqlDbType.Int)
                command.Parameters("@Member_Id").Value = memberId
                command.Parameters.Add("@Locked", SqlDbType.Bit)
                command.Parameters("@Locked").Direction = ParameterDirection.Output
                connection.Open()
                command.ExecuteNonQuery()
                ' If the member's account is locked return true.
                If CBool(command.Parameters("@Locked").Value) = True Then
                    Return True
                End If
            Catch ex As Exception
                ' If an exception occurs return true.
                Return True
            Finally
                connection.Close()
            End Try
            ' If the member's account is not locked return false.
            Return False
        End Function

        ''' <summary>
        ''' Gets the main character id associated with the member's account.
        ''' </summary>
        ''' <param name="memberId">The member's id number.</param>
        ''' <returns>The main charater's id associated with the member's account.</returns>
        Private Shared Function GetMain(ByVal memberId As Integer) As Integer
            Try
                command = New SqlCommand("sp_public_checkMainCharacter", connection)
                command.CommandType = CommandType.StoredProcedure
                command.Parameters.Add("@Member_Id", SqlDbType.Int)
                command.Parameters("@Member_Id").Value = memberId
                command.Parameters.Add("@MainCharacter", SqlDbType.Int)
                command.Parameters("@MainCharacter").Direction = ParameterDirection.Output
                connection.Open()
                command.ExecuteNonQuery()
                If Not command.Parameters("@MainCharacter").Value Is DBNull.Value Then
                    ' Assign the members chosen mains ID to the users session.
                    HttpContext.Current.Session("MainCharacter") = CInt(command.Parameters("@MainCharacter").Value)

                    ' Return the members main character id.
                    Return CInt(command.Parameters("@MainCharacter").Value)
                End If
            Catch ex As Exception

            Finally
                connection.Close()
            End Try
            ' If no main character has been assigned then assign 0 as the main characters id in the users session.
            HttpContext.Current.Session("MemberMain") = 0

            ' If no main character was found return 0 as the members main character.
            Return 0
        End Function

#End Region

#Region " Status updates "

        ''' <summary>
        ''' Updates the member's status in the database.
        ''' </summary>
        ''' <param name="memberId">The member's id number.</param>
        Private Shared Sub UpdateStatus(ByVal memberId As Integer)
            ' Update the last active time in the database for this member to the time now.
            Try
                command = New SqlCommand("sp_public_updateLastActive", connection)
                command.CommandType = CommandType.StoredProcedure
                command.Parameters.Add("@Member_Id", SqlDbType.Bit)
                command.Parameters("@Member_Id").Value = memberId
                connection.Open()
                command.ExecuteNonQuery()
            Catch ex As Exception

            Finally
                connection.Close()
            End Try

            ' Later on we can add code here to track where members are on the site and so on...

        End Sub

#End Region

#Region " Logout the user "

        ''' <summary>
        ''' Logs a member out of the system.
        ''' </summary>
        Public Shared Sub LogoutMember()
            ' Destroy the current session.
            HttpContext.Current.Session.Abandon()

            ' Destroy the "Login" cookie.
            Dim cookie As HttpCookie = HttpContext.Current.Request.Cookies("Account")
            If cookie IsNot Nothing Then
                cookie.Expires = DateTime.Now.AddDays(-1D)
                HttpContext.Current.Response.Cookies.Add(cookie)
            End If
        End Sub

#End Region

    End Class
End Namespace
